package com.literature.search.file.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.literature.search.core.entity.CollectFileRelationship;
import com.literature.search.core.entity.FileDocument;
import com.literature.search.core.entity.LikeFileRelationship;
import com.literature.search.core.enums.FileEsStateEnum;
import com.literature.search.core.utils.LocalDateTimeConvertUtil;
import com.literature.search.core.utils.RedisKeyUtils;
import com.literature.search.core.utils.sp.SpUserUtil;
import com.literature.search.file.entity.vo.CollectionListVO;
import com.literature.search.file.entity.vo.LikeListVO;
import com.literature.search.file.service.IFileService;
import com.literature.search.file.service.LikeAndCollectionService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName LikeAndCollectionServiceImpl
 * @Description TODO
 * @Author abin
 * @Date 2023/3/24 17:29
 * @Version 1.0
 */
@Service
public class LikeAndCollectionServiceImpl implements LikeAndCollectionService {

    public static final String USER_ID = "user_id"; //用户Id

    public static final String FILE_ID = "_id"; //文件Id

    public static final String COLLECTION_RECORD_COLLECTION_NAME = "collect_file_relationship"; //收藏集合名称

    public static final String LIKE_RECORD_COLLECTION_NAME = "like_file_relationship"; //点赞集合名称

    public static final String FILE_COLLECTION_NAME = "file_document"; //文件集合名称


    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private IFileService fileService;

    @Override
    public Integer getLikedCount(String fileId){
        return  (Integer) redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT,fileId);
    }

    @Override
    public Integer getLikeStatus(String fileId, String likeUserId) {
        if (redisTemplate.opsForHash().hasKey(RedisKeyUtils.MAP_KEY_USER_LIKED, RedisKeyUtils.getLikedKey(fileId, likeUserId))){
            JSONObject jsonObject = (JSONObject) redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_LIKED, RedisKeyUtils.getLikedKey(fileId, likeUserId));
            HashMap<String, Object> map = JSONObject.parseObject(String.valueOf(jsonObject),HashMap.class);
            return (Integer) map.get("status");
        }
        return 0;
    }

    @Override
    public boolean like(String fileId, String userId) {
        // 生成key
        String key = RedisKeyUtils.getLikedKey(fileId, userId);
        // 封装value 喜欢状态 更新时间
        HashMap<String,Object> map = new HashMap<>();
        map.put("status",1);
        map.put("updateTime", System.currentTimeMillis());
        FileDocument fileDocument = mongoTemplate.findOne(new Query(Criteria.where("_id").is(fileId)),FileDocument.class,FILE_COLLECTION_NAME);
        if (fileDocument.getFileEsState() == FileEsStateEnum.SUCCESS) {
            in_decrementLikedCount(fileId, 1);
            redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, map);
            return true;
        }
        return false;
    }

    @Override
    public boolean dislike(String fileId, String likeUserId) {
        // 生成key
        String key = RedisKeyUtils.getLikedKey(fileId, likeUserId);
        // 封装value 喜欢状态 更新时间
        HashMap<String,Object> map = new HashMap<>();
        map.put("status",0);
        map.put("updateTime", System.currentTimeMillis());// 存入当前时间戳
        FileDocument fileDocument = mongoTemplate.findOne(new Query(Criteria.where("_id").is(fileId)),FileDocument.class,FILE_COLLECTION_NAME);
        if (fileDocument.getFileEsState() == FileEsStateEnum.SUCCESS) {
            in_decrementLikedCount(fileId, -1);
            redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, map);
            return true;
        }
        return false;
    }

    @Override
    public void deleteLikedFromRedis(String fileId, String likeUserId) {
        String key = RedisKeyUtils.getLikedKey(fileId, likeUserId);
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
    }

    @Override
    public void in_decrementLikedCount(String fileId, long delta) {
        redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, fileId, delta);
    }

    @Override
    public void likeRecord2DB() throws InterruptedException {
        // 批量获取缓存中的点赞数据
        List<LikeFileRelationship> list = getLikedRecordFromRedis();
        if (CollectionUtils.isEmpty(list)) {// 为空，不写入
            return;
        }
        for (LikeFileRelationship item : list) {
            LikeFileRelationship likeFileRelationship = mongoTemplate.findOne(new Query(Criteria.where("fileId").is(item.getFileId()).and("userId").is(item.getUserId())), LikeFileRelationship.class, LIKE_RECORD_COLLECTION_NAME);// 在数据库中查询

            if (likeFileRelationship == null) {// 无记录，新增
                if (!save(item)) {
                    System.out.println("新增点赞数据失败！");
                    return;
                }
            }
            else {// 有记录，更新
                // 判断数据库中点赞时间与缓存中点赞时间一致性
                if (likeFileRelationship.getCreateDate() != item.getCreateDate()) {// 不一致，进行持久化
                    Update update = new Update();
                    update.set("state", item.getState());
                    update.set("createDate", item.getCreateDate());
                    mongoTemplate.updateFirst(new Query(Criteria.where("fileId").is(item.getFileId()).and("userId").is(item.getUserId())), update, LikeFileRelationship.class, LIKE_RECORD_COLLECTION_NAME);
                }
            }
        }
    }

    @Override
    public Boolean save(LikeFileRelationship likeFileRelationship) {
        LikeFileRelationship result = mongoTemplate.save(likeFileRelationship, LIKE_RECORD_COLLECTION_NAME);
        if (result != null) {
            return true;
        }
        return false;
    }

    @Override
    public List<LikeFileRelationship> getLikedRecordFromRedis() {
        // scan 读取数据，比key匹配优雅
        Cursor<Map.Entry<String, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE);

        List<LikeFileRelationship> list = new ArrayList<>();
        while (cursor.hasNext()) {
            Map.Entry<String, Object> entry = cursor.next();
            String key = (String) entry.getKey();
            //分离出 fileId，userId, 解析value
            String[] split = key.split(":");
            String fileId = split[0];
            String userId = split[1];
            HashMap<String, Object> map = JSONObject.parseObject(entry.getValue().toString(), HashMap.class);
            Integer status = (Integer) map.get("status");
            long updateTimeStamp = (long) map.get("updateTime");
            String updateTime = LocalDateTimeConvertUtil.getDateTimeOfTimestamp(updateTimeStamp).toString();// 时间戳转为LocalDateTime再转换为String

            //组装成 LikeFileRelationship 对象
            LikeFileRelationship likeFileRelationship = new LikeFileRelationship(fileId, userId, status, updateTime);
            list.add(likeFileRelationship);

            //存到 list 后从 Redis 中清理缓存
//            redisHash.delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key);
        }
        return list;
    }

    @Override
    public List<LikeListVO> queryUserLikeList() {
        String userId = SpUserUtil.getCurrUser().getId();
        Query query = new Query(Criteria.where(USER_ID).is(userId));
        List<LikeFileRelationship> likeFileRelationships = mongoTemplate.find(query, LikeFileRelationship.class,LIKE_RECORD_COLLECTION_NAME);
        if (CollectionUtils.isEmpty(likeFileRelationships)){
            System.out.println("列表为空");
        }
        List<String> fileIds = likeFileRelationships.stream().map(LikeFileRelationship::getFileId).collect(Collectors.toList());
        List<FileDocument> fileDocuments = mongoTemplate.find(new Query(Criteria.where(FILE_ID).in(fileIds)), FileDocument.class,FILE_COLLECTION_NAME);
        //将查询出来的点赞收藏关系映射为LikeListVO
        List<LikeListVO> likeListVOList = likeFileRelationships.stream()
                .map(LikeListVO::new)
                .collect(Collectors.toList());

        //根据FileId进行分组
        Map<String, List<FileDocument>> FileDocumentMap = fileDocuments.stream().collect(Collectors.groupingBy(FileDocument::getId));

        //对上传者和缩略图信息进行设置
        for (Map.Entry<String, List<FileDocument>> entry : FileDocumentMap.entrySet()) {
            likeListVOList.stream().filter(likeListVO -> likeListVO.getFileId().equals(entry.getKey()))
                    .findFirst()
                    .ifPresent(likeListVO -> {
                        FileDocument fileDocument = entry.getValue().get(0);
                        likeListVO.setFileName(fileDocument.getFileName());
                        likeListVO.setUploader(fileDocument.getUserName());
                        likeListVO.setThumbnailId(fileDocument.getThumbId());
                    });
        }
        return likeListVOList;
    }

    @Override
    public Integer getCollectedCount(String fileId){
        return  (Integer) redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_COLLECTED_COUNT,fileId);
    }

    @Override
    public Integer getCollectStatus(String fileId, String likeUserId) {
        if (redisTemplate.opsForHash().hasKey(RedisKeyUtils.MAP_KEY_USER_COLLECTED, RedisKeyUtils.getLikedKey(fileId, likeUserId))){
            JSONObject jsonObject = (JSONObject) redisTemplate.opsForHash().get(RedisKeyUtils.MAP_KEY_USER_COLLECTED, RedisKeyUtils.getLikedKey(fileId, likeUserId));
            HashMap<String, Object> map = JSONObject.parseObject(String.valueOf(jsonObject),HashMap.class);
            return (Integer) map.get("status");
        }
        return 0;
    }

    @Override
    public boolean collect(String fileId, String userId) {
        // 生成key
        String key = RedisKeyUtils.getCollectedKey(fileId, userId);
        // 封装value 喜欢状态 更新时间
        HashMap<String,Object> map = new HashMap<>();
        map.put("status",1);
        map.put("updateTime", System.currentTimeMillis());
        FileDocument fileDocument = mongoTemplate.findOne(new Query(Criteria.where("_id").is(fileId)),FileDocument.class,FILE_COLLECTION_NAME);
        if (fileDocument.getFileEsState() == FileEsStateEnum.SUCCESS) {
            in_decrementCollectedCount(fileId, 1);
            redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_COLLECTED, key, map);
            return true;
        }
        return false;
    }

    @Override
    public boolean cancelCollect(String fileId, String likeUserId) {
        // 生成key
        String key = RedisKeyUtils.getCollectedKey(fileId, likeUserId);
        // 封装value 喜欢状态 更新时间
        HashMap<String,Object> map = new HashMap<>();
        map.put("status",0);
        map.put("updateTime", System.currentTimeMillis());// 存入当前时间戳
        FileDocument fileDocument = mongoTemplate.findOne(new Query(Criteria.where("_id").is(fileId)),FileDocument.class,FILE_COLLECTION_NAME);
        if (fileDocument.getFileEsState() == FileEsStateEnum.SUCCESS) {
            in_decrementCollectedCount(fileId, -1);
            redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_COLLECTED, key, map);
            return true;
        }
        return false;
    }

    @Override
    public void deleteCollectdFromRedis(String fileId, String likeUserId) {
        String key = RedisKeyUtils.getLikedKey(fileId, likeUserId);
        redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_COLLECTED, key);
    }

    @Override
    public void in_decrementCollectedCount(String fileId, long delta) {
        redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_COLLECTED_COUNT, fileId, delta);
    }

    @Override
    public void collectRecord2DB() throws InterruptedException {
        // 批量获取缓存中的点赞数据
        List<CollectFileRelationship> list = getCollectedRecordFromRedis();
        if (CollectionUtils.isEmpty(list)) {// 为空，不写入
            return;
        }
        for (CollectFileRelationship item : list) {
            CollectFileRelationship collectFileRelationship = mongoTemplate.findOne(new Query(Criteria.where("fileId").is(item.getFileId()).and("userId").is(item.getUserId())), CollectFileRelationship.class, COLLECTION_RECORD_COLLECTION_NAME);// 在数据库中查询

            if (collectFileRelationship == null) {// 无记录，新增
                if (!saveCollection(item)) {
                    System.out.println("新增点赞数据失败！");
                    return;
                }
            }
            else {// 有记录，更新
                // 判断数据库中点赞时间与缓存中点赞时间一致性
                if (collectFileRelationship.getCreateDate() != item.getCreateDate()) {// 不一致，进行持久化
                    Update update = new Update();
                    update.set("state", item.getState());
                    update.set("createDate", item.getCreateDate());
                    mongoTemplate.updateFirst(new Query(Criteria.where("fileId").is(item.getFileId()).and("userId").is(item.getUserId())), update, CollectFileRelationship.class, COLLECTION_RECORD_COLLECTION_NAME);
                }
            }
        }
    }

    @Override
    public Boolean saveCollection(CollectFileRelationship collectFileRelationship) {
        CollectFileRelationship result = mongoTemplate.save(collectFileRelationship, COLLECTION_RECORD_COLLECTION_NAME);
        if (result != null) {
            return true;
        }
        return false;
    }

    @Override
    public List<CollectFileRelationship> getCollectedRecordFromRedis() {
        // scan 读取数据，比key匹配优雅
        Cursor<Map.Entry<String, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_COLLECTED, ScanOptions.NONE);

        List<CollectFileRelationship> list = new ArrayList<>();
        while (cursor.hasNext()) {
            Map.Entry<String, Object> entry = cursor.next();
            String key = (String) entry.getKey();
            //分离出 fileId，userId, 解析value
            String[] split = key.split(":");
            String fileId = split[0];
            String userId = split[1];
            HashMap<String, Object> map = JSONObject.parseObject(entry.getValue().toString(), HashMap.class);
            Integer status = (Integer) map.get("status");
            long updateTimeStamp = (long) map.get("updateTime");
            String updateTime = LocalDateTimeConvertUtil.getDateTimeOfTimestamp(updateTimeStamp).toString();// 时间戳转为LocalDateTime再转换为String

            //组装成 CollectFileRelationship 对象
            CollectFileRelationship collectFileRelationship = new CollectFileRelationship(fileId, userId, status, updateTime);
            list.add(collectFileRelationship);
        }
        return list;
    }


    //获取用户收藏列表
    @Override
    public List<CollectionListVO> queryUserCollectionList() {
        String userId = SpUserUtil.getCurrUser().getId();
        Query query = new Query(Criteria.where(USER_ID).is(userId));
        List<CollectFileRelationship> collectFileRelationships = mongoTemplate.find(query, CollectFileRelationship.class,COLLECTION_RECORD_COLLECTION_NAME);
        List<String> fileIds = collectFileRelationships.stream().map(CollectFileRelationship::getFileId).collect(Collectors.toList());
        List<FileDocument> fileDocuments = mongoTemplate.find(new Query(Criteria.where(FILE_ID).in(fileIds)), FileDocument.class,FILE_COLLECTION_NAME);
        //将查询出来的用户收藏关系映射为CollectionListVO
        List<CollectionListVO> collectionListVOList = collectFileRelationships.stream()
                .map(CollectionListVO::new)
                .collect(Collectors.toList());

        //根据FileId进行分组
        Map<String, List<FileDocument>> FileDocumentMap = fileDocuments.stream().collect(Collectors.groupingBy(FileDocument::getId));

        //对上传者和缩略图信息进行设置
        for (Map.Entry<String, List<FileDocument>> entry : FileDocumentMap.entrySet()) {
            collectionListVOList.stream().filter(collectionListVO -> collectionListVO.getFileId().equals(entry.getKey()))
                    .findFirst()
                    .ifPresent(collectionListVO -> {
                        FileDocument fileDocument = entry.getValue().get(0);
                        collectionListVO.setFileName(fileDocument.getFileName());
                        collectionListVO.setUploader(fileDocument.getUserName());
                        collectionListVO.setThumbnailId(fileDocument.getThumbId());
                    });
        }
        return collectionListVOList;
    }

    @Override
    public List<String> getLikedFileIds(){
        String key = RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT;
        Set<String> set = redisTemplate.opsForHash().keys(key);
        return set.stream().collect(Collectors.toList());
    }

    @Override
    public List<String> getCollectdFileIds(){
        String key = RedisKeyUtils.MAP_KEY_USER_COLLECTED_COUNT;
        Set<String> set = redisTemplate.opsForHash().keys(key);
        return set.stream().collect(Collectors.toList());
    }
}
